import os
import json
import logging
import requests
import pandas as pd
import queue
import threading
import time

# 日志队列，用于实时日志流
log_queue = queue.Queue()

class QueueHandler(logging.Handler):
    def emit(self, record):
        try:
            msg = self.format(record)
            log_queue.put(msg)
        except Exception:
            self.handleError(record)

logger = logging.getLogger("zentao_import")
logger.setLevel(logging.INFO)
for handler in logger.handlers[:]:
    logger.removeHandler(handler)
queue_handler = QueueHandler()
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
queue_handler.setFormatter(formatter)
logger.addHandler(queue_handler)

def get_token(ZENTAO_URL, account, password):
    logger.info("开始获取Token...")
    try:
        response = requests.post(
            f"{ZENTAO_URL}/tokens",
            json={"account": account, "password": password},
            timeout=10
        )
        response.raise_for_status()
        token = response.json().get("token")
        logger.info("Token获取成功。")
        return token
    except Exception as e:
        logger.error(f"获取Token失败: {e}")
        return None

def get_existing_cases(ZENTAO_URL, token, product_id):
    logger.info("开始获取现有用例...")
    headers = {"Token": token}
    existing_cases = {}
    try:
        response = requests.get(
            f"{ZENTAO_URL}/products/{product_id}/testcases?limit=5000",
            headers=headers,
            timeout=10
        )
        response.raise_for_status()
        response_json = response.json()
        if response_json.get("testcases"):
            for case in response_json["testcases"]:
                case_id = case["id"]
                case_title = case["title"]
                existing_cases[case_title] = case_id
        logger.info(f"获取到 {len(existing_cases)} 个用例。")
        return existing_cases
    except Exception as e:
        logger.error(f"获取用例失败: {e}")
        return None

def parse_steps(steps_str, expected_str):
    logger.info(f"开始解析步骤数据... 预期：{expected_str}")
    steps = []
    try:
        steps_json = json.loads(steps_str)
        for step in steps_json:
            steps.append({
                "desc": step.get("desc", ""),
                "expect": expected_str
            })
    except json.JSONDecodeError:
        logger.warning(f"步骤不是有效的JSON格式，按纯文本处理: {steps_str}")
        step_lines = steps_str.split("\n")
        expect_lines = expected_str.split("\n") if expected_str else []
        for i, line in enumerate(step_lines):
            expect = expect_lines[i].strip() if i < len(expect_lines) else ""
            steps.append({
                "desc": line.strip(),
                "expect": expect
            })
    steps = [step for step in steps if step["desc"]]
    logger.info(f"解析后的步骤数据: {steps}")
    return steps

def import_cases(ZENTAO_URL, product_id, csv_path, token):
    logger.info("导入csv...")
    headers = {"Token": token, "Content-Type": "application/json"}
    new_cases = {}

    if not os.path.isfile(csv_path):
        error_msg = f"错误：文件 '{csv_path}' 不存在。"
        logger.error(error_msg)
        return error_msg

    logger.info(f"正在读取CSV文件: {csv_path}")
    REQUIRED_CSV_FIELDS = ["用例名称", "优先级", "用例类型", "步骤"]
    total_rows = 0
    first_chunk = True

    try:
        for chunk in pd.read_csv(csv_path, encoding='GBK', sep=None, engine='python', chunksize=20):
            if first_chunk:
                header_fields = list(chunk.columns)
                logger.info(f"实际读取到的表头字段：{header_fields}")
                missing_fields = [field for field in REQUIRED_CSV_FIELDS if field not in header_fields]
                if missing_fields:
                    error_msg = f"错误：CSV缺少必要的表头字段: {', '.join(missing_fields)}"
                    logger.error(error_msg)
                    return error_msg
                first_chunk = False
            for index, row in chunk.iterrows():
                total_rows += 1
                cleaned_row = {}
                for k in row.index:
                    value = row[k]
                    cleaned_value = "" if pd.isna(value) else str(value).strip().replace('""', '"')
                    cleaned_row[k] = cleaned_value
                for field in REQUIRED_CSV_FIELDS:
                    if field not in cleaned_row or cleaned_row[field] == "":
                        error_msg = f"错误：行 {total_rows} 中缺少或为空必要字段 '{field}'，行内容: {cleaned_row}"
                        logger.error(error_msg)
                        return error_msg
                title = cleaned_row["用例名称"]
                if title in new_cases:
                    error_msg = f"错误：标题重复 '{title}'"
                    logger.error(error_msg)
                    return error_msg
                logger.info(f"正在解析用例: {title}")
                case_data = {
                    "title": cleaned_row["用例名称"],
                    "pri": int(cleaned_row["优先级"]),
                    "type": cleaned_row["用例类型"],
                    "steps": parse_steps(cleaned_row["步骤"], cleaned_row.get("预期", "")),
                    "module": cleaned_row.get("所属模块", ""),
                    "precondition": cleaned_row.get("前置条件", ""),
                    "keywords": cleaned_row.get("关键词", ""),
                    "stage": cleaned_row.get("适用阶段", "")
                }
                new_cases[title] = case_data
            logger.info(f"已处理 {total_rows} 行数据")
            time.sleep(0.1)
    except Exception as e:
        error_msg = f"读取CSV文件时出错: {e}"
        logger.error(error_msg)
        return error_msg

    logger.info(f"CSV文件读取完成，共读取到 {len(new_cases)} 个用例。")

    # 获取现有用例
    existing_cases = get_existing_cases(ZENTAO_URL, token, product_id)
    if existing_cases is None:
        error_msg = "获取现有用例失败。"
        logger.error(error_msg)
        return error_msg

    new_count = 0
    update_count = 0
    delete_count = 0
    new_fail_count = 0
    update_fail_count = 0
    delete_fail_count = 0

    for title, case_data in new_cases.items():
        if title in existing_cases:
            case_id = existing_cases[title]
            actual_case_id = case_id.split("_")[1] if "_" in case_id else case_id
            logger.info(f"正在更新用例: {title} (ID: {actual_case_id})")
            try:
                response = requests.put(
                    f"{ZENTAO_URL}/testcases/{actual_case_id}",
                    headers=headers,
                    json=case_data,
                    timeout=60
                )
                response.raise_for_status()
                update_count += 1
                logger.info(f"更新成功: {title} (ID: {actual_case_id})")
            except Exception as e:
                update_fail_count += 1
                logger.error(f"更新失败 {title}: {e}")
        else:
            logger.info(f"正在创建新用例: {title}")
            try:
                response = requests.post(
                    f"{ZENTAO_URL}/products/{product_id}/testcases",
                    headers=headers,
                    json=case_data,
                    timeout=60
                )
                response.raise_for_status()
                new_count += 1
                new_id = response.json().get("id")
                logger.info(f"新增成功: {title} (ID: {new_id})")
            except Exception as e:
                new_fail_count += 1
                logger.error(f"新增失败 {title}: {e}")

    for title, case_id in existing_cases.items():
        if title not in new_cases:
            actual_case_id = case_id.split("_")[1] if "_" in case_id else case_id
            logger.info(f"正在删除用例：{title}")
            try:
                response = requests.delete(
                    f"{ZENTAO_URL}/testcases/{actual_case_id}",
                    headers=headers,
                    timeout=60
                )
                response_json = response.json()
                if response.status_code == 200:
                    if response_json.get("message") == "success":
                        delete_count += 1
                        logger.info(f"删除成功：{title}")
                    else:
                        delete_fail_count += 1
                        logger.error(f"删除失败 {title}: 返回的message: {response_json.get('message')}")
                else:
                    delete_fail_count += 1
                    logger.error(f"删除失败 {title}: 状态码 {response.status_code}, 返回内容: {response.text}")
            except Exception as e:
                delete_fail_count += 1
                logger.error(f"删除失败 {title}: {e}")

    summary_msg = (
        f"<strong>操作完成：共新建<span style='color: green;'>{new_count}</span>个用例，"
        f"共更新<span style='color: blue;'>{update_count}</span>个用例，"
        f"共删除<span style='color: purple;'>{delete_count}</span>个用例。"
        f"失败统计：新增失败<span style='color: red;'>{new_fail_count}</span>个，"
        f"更新失败<span style='color: red;'>{update_fail_count}</span>个，"
        f"删除失败<span style='color: red;'>{delete_fail_count}</span>个。"
        f"<br>从CSV读取到的用例个数：<span style='color: black;'>{len(new_cases)}</span>；"
        f"新增和更新的用例总数：<span style='color: black;'>{new_count + update_count}</span>。</strong>"
    )

    logger.info(summary_msg)
    return summary_msg


def background_job(ZENTAO_URL, account, password, product_id, csv_path):
    token = get_token(ZENTAO_URL, account, password)
    if token is None:
        logger.error("获取Token失败，请检查账号或密码。")
        if os.path.exists(csv_path):
            os.remove(csv_path)
        return
    result = import_cases(ZENTAO_URL, product_id, csv_path, token)
    logger.info(result)
    if os.path.exists(csv_path):
        os.remove(csv_path)
